/**************************************************************************************************
* Filename:			DirectInput.cpp
* Date:				03/03/10
* Mod. Date:		03/05/10
* Mod. Initials:	MG
* Author:			Malcolm J. Gruber
* Purpose:			An outlying wrapper for Direct Input for keyboard and mouse.
**************************************************************************************************/

#include "DirectInput.h"

DirectInput DirectInput::m_Instance;

DirectInput::DirectInput(void)
{
	m_lpDICom = NULL;
	m_pKeyboard = NULL;
	m_pMouse = NULL;
}

DirectInput::~DirectInput(void)
{
	ShutdownDirectInput();
}

bool DirectInput::InitKeyboard(HWND hWnd, bool bExclusive)
{
	if( m_pKeyboard )
	{
		DIERRMSG(hWnd, "Keyboard has already been initialized");
		return false;
	}

	m_pKeyboard = new DirectKeyboard(m_lpDICom, hWnd, bExclusive);

	if( m_pKeyboard == NULL )
		return false;

	return true;
}

bool DirectInput::InitMouse(HWND hWnd, bool bExclusive)
{
	if( m_pMouse )
	{
		DIERRMSG(hWnd, "Mouse has already been initialized");
		return false;
	}

	m_pMouse = new DirectMouse(m_lpDICom, hWnd, true/*bExclusive*/);

	if( m_pMouse == NULL )
		return false;

	return true;
}

bool DirectInput::InitDirectInput(HWND hWnd, HINSTANCE hInstance, uint32_t unDevices, uint32_t unExclusive)
{
	if( FAILED( DirectInput8Create(hInstance, DIRECTINPUT_HEADER_VERSION, IID_IDirectInput8, (void**)&m_lpDICom, NULL) ))
	{
		return false;
	}

	if( unDevices & DI_KEYBOARD )
	{
		if( !InitKeyboard(hWnd, (unExclusive & DI_KEYBOARD) ? true : false ))
			return false;
	}

	if( unDevices & DI_MOUSE )
	{
		if( !InitMouse(hWnd, (unExclusive & DI_MOUSE) ? true : false ))
			return false;
	}

	return true;
}

DirectInput* DirectInput::GetInstance(void)
{
	return &m_Instance;
}

void DirectInput::ShutdownDirectInput(void)
{
	UnacquireAll();

	SAFE_DELETE(m_pKeyboard);
	SAFE_DELETE(m_pMouse);
}

void DirectInput::ReadDevices(void)
{
	if( m_pKeyboard )
	{
		m_pKeyboard->ReadDevice();
		m_pKeyboard->ReadBufferedDevice();
	}

	if( m_pMouse )
	{
		m_pMouse->ReadDevice();
		m_pMouse->ReadBufferedDevice();
	}
}

void DirectInput::ClearInput(void)
{
	if( m_pKeyboard )
	{
		m_pKeyboard->ClearKeys();
	}

	if( m_pMouse )
	{
		m_pMouse->ClearMouseButtons();
	}
}

void DirectInput::AcquireAll(void)
{
	if( m_pKeyboard )
	{
		m_pKeyboard->Acquire();
	}

	if( m_pMouse )
	{
		m_pMouse->Acquire();
	}
}

void DirectInput::UnacquireAll(void)
{
	if( m_pKeyboard )
	{
		m_pKeyboard->Unacquire();
	}

	if( m_pMouse )
	{
		m_pMouse->Unacquire();
	}
}

bool DirectInput::KeyDown(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyDown(ucDIKey & 0x00FF));
}

bool DirectInput::KeyPressed(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyPressed(ucDIKey & 0x00FF));
}

bool DirectInput::KeyUp(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyUp(ucDIKey & 0x00FF));
}

bool DirectInput::KeyReleased(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyReleased(ucDIKey & 0x00FF));
}

bool DirectInput::KeyPressedEx(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyPressedEx(ucDIKey & 0x00FF));
}

bool DirectInput::KeyReleasedEx(unsigned char ucDIKey)
{
	return (m_pKeyboard && m_pKeyboard->KeyReleasedEx(ucDIKey & 0x00FF));
}

uint16_t DirectInput::CheckKeys(void)
{
	return (m_pKeyboard) ? m_pKeyboard->CheckKeys() : 0;
}

uint16_t DirectInput::CheckKeysEx(void)
{
	return (m_pKeyboard) ? m_pKeyboard->CheckKeysEx() : 0;
}

int32_t DirectInput::CheckMouseButton(void)
{
	return (m_pMouse) ? m_pMouse->CheckMouseButtons() : 0;
}

int32_t DirectInput::CheckMouseButtonEx(void)
{
	return (m_pMouse) ? m_pMouse->CheckMouseButtonsEx() : 0;
}

int16_t DirectInput::GetDIKCode(void)
{
	return (m_pKeyboard) ? m_pKeyboard->GetDIKCode() : 0;
}

int16_t DirectInput::GetDIKCodeEx(void)
{
	return (m_pKeyboard) ? m_pKeyboard->GetDIKCodeEx() : 0;
}

bool DirectInput::MouseButtonDown(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonDown(ucDIButton & 0x00FF));
}

bool DirectInput::MouseButtonPressed(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonPressed(ucDIButton & 0x00FF));
}

bool DirectInput::MouseButtonUp(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonUp(ucDIButton & 0x00FF));
}

bool DirectInput::MouseButtonReleased(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonReleased(ucDIButton & 0x00FF));
}

bool DirectInput::MouseButtonPressedEx(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonPressedEx(ucDIButton & 0x00FF));
}

bool DirectInput::MouseButtonReleasedEx(unsigned char ucDIButton)
{
	return (m_pMouse && m_pMouse->ButtonReleasedEx(ucDIButton & 0x00FF));
}

int32_t DirectInput::MouseSpeedX(void)
{
	return (m_pMouse) ? m_pMouse->MouseSpeedX() : 0;
}

int32_t DirectInput::MouseSpeedY(void)
{	
	return (m_pMouse) ? m_pMouse->MouseSpeedY() : 0;
}

int32_t DirectInput::MouseSpeedWheel(void)
{
	return (m_pMouse) ? m_pMouse->WheelSpeed() : 0;
}

int32_t DirectInput::GetNumMouseButtons(void)
{
	return (m_pMouse) ? m_pMouse->GetNumMouseButtons() : 0;
}

int32_t DirectInput::GetMousePosX(void)
{
	return (m_pMouse) ? m_pMouse->GetMousePosX() : 0;
}

int32_t DirectInput::GetMousePosY(void)
{
	return (m_pMouse) ? m_pMouse->GetMousePosY() : 0;
}

void DirectInput::SetMousePosX(int32_t nPosX)
{
	if( m_pMouse )
		m_pMouse->SetMousePosX(nPosX);
}

void DirectInput::SetMousePosY(int32_t nPosY)
{
	if( m_pMouse )
		m_pMouse->SetMousePosY(nPosY);
}

void DirectInput::SetMouseSensitivity(int32_t nSensValue)
{
	if ( m_pMouse )
		m_pMouse->SetMouseSensitivity(nSensValue);
}

int32_t DirectInput::GetMouseSensitivity(void)
{
	if ( m_pMouse )
		return m_pMouse->GetMouseSensitivity();
	return 0;
}

/*************************************************************************
* DirectKeyboard - Keyboard input
*************************************************************************/

DirectKeyboard::DirectKeyboard(LPDIRECTINPUT8 lpDI, HWND hWnd, bool bExclusive)
{
	if( FAILED( lpDI->CreateDevice(GUID_SysKeyboard, &m_lpDevice, NULL) ))
	{
		DIERRMSG(hWnd, "Failed to create keyboard device.");
	}

	if( FAILED(	m_lpDevice->SetDataFormat(&c_dfDIKeyboard) ))
	{
		DIERRMSG(hWnd, "Failed to set data format on keyboard.");
	}

	DWORD dwFlags = DISCL_FOREGROUND;

	if( bExclusive )
	{
		dwFlags |= DISCL_EXCLUSIVE;
	}
	else
	{
		dwFlags |= DISCL_NONEXCLUSIVE | DISCL_NOWINKEY;
	}

	if( FAILED( m_lpDevice->SetCooperativeLevel(hWnd, dwFlags) ))
	{
		DIERRMSG(hWnd, "Failed to set cooperative level on keyboard.");
	}

	DIPROPDWORD dipdw;

	memset(&dipdw, 0, sizeof(dipdw));

	dipdw.diph.dwSize = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj = 0;
	dipdw.diph.dwHow = DIPH_DEVICE;
	dipdw.dwData = g_nBufferSize;

	if( FAILED( m_lpDevice->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) ))
	{
		DIERRMSG(hWnd, "Could not set the Properties for buffered input for the keyboard.");
	}

	if( FAILED( m_lpDevice->Acquire() ))
	{
		//DIERRMSG(hWnd, "Failed to acquire the keyboard.");
	}

	ClearKeys();

	m_keyLayout = GetKeyboardLayout(0);
}

bool DirectKeyboard::ReadDevice(void)
{
	if( !m_lpDevice )
		return false;

	memcpy(m_ucPrevKeysPressed, m_ucKeysPressed, sizeof(m_ucPrevKeysPressed));

	if( FAILED( m_lpDevice->GetDeviceState(sizeof(m_ucKeysPressed), (LPVOID)m_ucKeysPressed) ))
	{
		if( FAILED( m_lpDevice->Acquire() ))
		{
			return false;
		}

		if( FAILED( m_lpDevice->GetDeviceState(sizeof(m_ucKeysPressed), (LPVOID)m_ucKeysPressed) ))
		{
			return false;
		}
	}

	return true;
}

bool DirectKeyboard::ReadBufferedDevice(void)
{
	if( !m_lpDevice )
	{
		return false;
	}

	ClearDataBuffer();

	if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
	{
		if( FAILED( m_lpDevice->Acquire() ))
		{
			return false;
		}

		ClearDataBuffer();

		if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
		{
			return false;
		}
	}

	return true;
}

bool DirectKeyboard::KeyDown(unsigned char ucKey)
{
	return (m_ucKeysPressed[ucKey] & 0x80) ? true : false;
}

bool DirectKeyboard::KeyPressed(unsigned char ucKey)
{
	return KeyDown(ucKey) && !(m_ucPrevKeysPressed[ucKey] & 0x80);
}

bool DirectKeyboard::KeyUp(unsigned char ucKey)
{
	return !KeyDown(ucKey);
}

bool DirectKeyboard::KeyReleased(unsigned char ucKey)
{
	return KeyUp(ucKey) && (m_ucPrevKeysPressed[ucKey] & 0x80);
}

bool DirectKeyboard::KeyPressedEx(unsigned char ucKey)
{
	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		if( (m_dodBuffer[i].dwOfs == ucKey) && (m_dodBuffer[i].dwData & 0x80) )
			return true;
	}

	return false;
}

bool DirectKeyboard::KeyReleasedEx(unsigned char ucKey)
{
	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		if( (m_dodBuffer[i].dwOfs == ucKey) && !(m_dodBuffer[i].dwData & 0x80) )
			return true;
	}

	return false;
}

unsigned char DirectKeyboard::CheckKeys(void)
{
	if( !GetKeyboardState(m_ucAsciiVals) )
	{
		return 0;
	}

	uint16_t usNum = 0;
	uint32_t unVKCode = 0;

	for(int k = 0; k < 256; k++)
	{
		if( KeyDown((unsigned char)k) )
		{
			unVKCode = MapVirtualKeyEx(k, 1, m_keyLayout);

			ToAsciiEx(unVKCode, k, m_ucAsciiVals, &usNum, 0, m_keyLayout);
		}
	}

	return (unsigned char)usNum;
}

unsigned char DirectKeyboard::CheckKeysEx(void)
{
	if( !GetKeyboardState(m_ucAsciiVals) )
	{
		return 0;
	}

	uint16_t usNum = 0;
	uint32_t unVKCode = 0;

	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		for(int k = 0; k < 256; k++)
		{
			if( (m_dodBuffer[i].dwOfs == k) && (m_dodBuffer[i].dwData & 0x80) )
			{
				unVKCode = MapVirtualKeyEx(m_dodBuffer[i].dwOfs, 1, m_keyLayout);
	
				ToAsciiEx(unVKCode, m_dodBuffer[i].dwOfs, m_ucAsciiVals, &usNum, 0, m_keyLayout);
			}
		}
	}

	return (unsigned char)usNum;
}

int8_t DirectKeyboard::GetDIKCode(void)
{
	int8_t ucNum = 0;

	for(int k = 0; k < 256; k++)
	{
		if( KeyDown(k) )
		{
			ucNum = (unsigned char)k;
			break;
		}
	}

	return ucNum;
}

int8_t DirectKeyboard::GetDIKCodeEx(void)
{
	int8_t ucNum = 0;

	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		for(int k = 0; k < 256; k++)
		{
			if( (m_dodBuffer[i].dwOfs == k) && (m_dodBuffer[i].dwData & 0x80) )
			{
				ucNum = k;
				break;
			}
		}
	}

	return ucNum;
}

/*************************************************************************
* DirectMouse - Mouse input
*************************************************************************/
DirectMouse::DirectMouse(LPDIRECTINPUT8 lpDI, HWND hWnd, bool bExclusive)
{
	if( FAILED( lpDI->CreateDevice(GUID_SysMouse, &m_lpDevice, NULL) ))
	{
		DIERRMSG(hWnd, "Failed to create mouse device.");
	}

	if( FAILED(	m_lpDevice->SetDataFormat(&c_dfDIMouse2) ))
	{
		DIERRMSG(hWnd, "Failed to set data format on mouse.");
	}

	DWORD dwFlags = DISCL_FOREGROUND;

	if( bExclusive )
	{
		dwFlags |= DISCL_EXCLUSIVE;
	}
	else
	{
		dwFlags |= DISCL_NONEXCLUSIVE;
	}

	if( FAILED( m_lpDevice->SetCooperativeLevel(hWnd, dwFlags) ))
	{
		DIERRMSG(hWnd, "Failed to set cooperative level on mouse.");
	}

	DIPROPDWORD dipdw;

	memset(&dipdw, 0, sizeof(dipdw));

	dipdw.diph.dwSize = sizeof(DIPROPDWORD);
	dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	dipdw.diph.dwObj = 0;
	dipdw.diph.dwHow = DIPH_DEVICE;
	dipdw.dwData = g_nBufferSize;

	if( FAILED( m_lpDevice->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) ))
	{
		DIERRMSG(hWnd, "Could not set the Properties for buffered input for the mouse.");
	}

	if( FAILED( m_lpDevice->Acquire() ))
	{
		//DIERRMSG(hWnd, "Failed to acquire the mouse.");
	}

	DIDEVCAPS didCaps;

	memset(&didCaps, 0, sizeof(didCaps));
	didCaps.dwSize = sizeof(didCaps);

	if( FAILED( m_lpDevice->GetCapabilities(&didCaps) ))
	{
		DIERRMSG(hWnd, "Failed to get mouse capabilities.");
	}

	if( didCaps.dwFlags & DIDC_POLLEDDATAFORMAT )
		int type = 8;
	if( didCaps.dwFlags & DIDC_POLLEDDEVICE )
		int type = 2;

	m_nNumButtons = didCaps.dwButtons;

	memset(&m_diMouseState, 0, sizeof(m_diMouseState));
	memset(&m_diPrevMouseState, 0, sizeof(m_diPrevMouseState));

	SetMousePosX(0);
	SetMousePosY(0);
	m_nSensitivity = DEFAULT_MOUSE_SENSITIVITY;
}

bool DirectMouse::ReadDevice(void)
{
	if( !m_lpDevice )
	{
		return false;
	}

	memcpy(&m_diPrevMouseState, &m_diMouseState, sizeof(m_diPrevMouseState));

	if( FAILED( m_lpDevice->GetDeviceState(sizeof(m_diMouseState), (LPVOID)&m_diMouseState) ))
	{
		if( FAILED( m_lpDevice->Acquire() ))
		{
			return false;
		}

		if( FAILED( m_lpDevice->GetDeviceState(sizeof(m_diMouseState), (LPVOID)&m_diMouseState) ))
		{
			return false;
		}
	}

	m_nPosX += (int32_t) (MouseSpeedX() * m_nSensitivity * 0.01f);
	m_nPosY += (int32_t) (MouseSpeedY() * m_nSensitivity * 0.01f);

	return true;
}

bool DirectMouse::ReadBufferedDevice(void)
{
	if( !m_lpDevice )
	{
		return false;
	}

	ClearDataBuffer();

	if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
	{
		if( FAILED( m_lpDevice->Acquire() ))
		{
			return false;
		}

		ClearDataBuffer();

		if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
		{
			return false;
		}
	}

	return true;
}

bool DirectMouse::ButtonDown(unsigned char ucButton)
{
	return ((m_diMouseState.rgbButtons[ucButton] & 0x80) ? true : false);
}

bool DirectMouse::ButtonPressed(unsigned char ucButton)
{
	return ButtonDown(ucButton) && !(m_diPrevMouseState.rgbButtons[ucButton] & 0x80);
}

bool DirectMouse::ButtonUp(unsigned char ucButton)
{
	return !ButtonDown(ucButton);
}

bool DirectMouse::ButtonReleased(unsigned char ucButton)
{
	return ButtonUp(ucButton) && (m_diPrevMouseState.rgbButtons[ucButton] & 0x80);
}

bool DirectMouse::ButtonPressedEx(unsigned char ucButton)
{
	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		if( (m_dodBuffer[i].dwOfs == (DWORD)(DIMOFS_BUTTON0 + ucButton)) && (m_dodBuffer[i].dwData & 0x80) )
		{
			return true;
		}
	}

	return false;
}

bool DirectMouse::ButtonReleasedEx(unsigned char ucButton)
{
	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		if( (m_dodBuffer[i].dwOfs == (DWORD)(DIMOFS_BUTTON0 + ucButton)) && !(m_dodBuffer[i].dwData & 0x80) )
		{
			return true;
		}
	}

	return false;
}

int32_t DirectMouse::CheckMouseButtons(void)
{
	int nButton = -1;

	for(int b = 0; b < GetNumMouseButtons(); b++)
	{
		if( ButtonPressed(b) )
		{
			nButton = b;
			break;
		}
	}

	return nButton;
}

int32_t DirectMouse::CheckMouseButtonsEx(void)
{
	int nButton = -1;

	for(DWORD i = 0; i < m_dwElementBuffer; i++)
	{
		for(int b = 0; b < GetNumMouseButtons(); b++)
		{
			if( (m_dodBuffer[i].dwOfs == (DWORD)(DIMOFS_BUTTON0 + b)) && !(m_dodBuffer[i].dwData & 0x80) )
			{
				nButton = b;
				break;
			}
		}
	}

	return nButton;
}

int32_t DirectMouse::MouseSpeedX(void)
{
	return (int32_t)m_diMouseState.lX;
}

int32_t DirectMouse::MouseSpeedY(void)
{
	return (int32_t)m_diMouseState.lY;
}

int32_t DirectMouse::WheelSpeed(void)
{
	return (int32_t)m_diMouseState.lZ;
}

int32_t DirectMouse::GetNumMouseButtons(void)
{
	return m_nNumButtons;
}

int32_t DirectMouse::GetMousePosX(void)
{
	return m_nPosX;
}

int32_t DirectMouse::GetMousePosY(void)
{
	return m_nPosY;
}

void DirectMouse::SetMousePosX(int32_t nPosX)
{
	m_nPosX = nPosX;
}

void DirectMouse::SetMousePosY(int32_t nPosY)
{
	m_nPosY = nPosY;
}

void DirectMouse::SetMouseSensitivity(int32_t nSensitivity)
{
	m_nSensitivity = nSensitivity;
}

int32_t DirectMouse::GetMouseSensitivity(void)
{
	return m_nSensitivity;
}
